home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / TSFAQP15.ZIP / FAQPAS2.TXT < prev    next >
Internet Message Format  |  1993-08-18  |  36KB

  1. From ts@uwasa.fi Wed Aug 18 00:00:00 1993
  2. Subject: FAQPAS2.TXT contents                  (All rights reserved)
  3.  
  4. FAQPAS2.TXT More frequently (and not so frequently) asked Turbo
  5. Pascal questions with Timo's answers.
  6.  
  7. ..................................................................
  8. Prof. Timo Salmi      Co-moderator of comp.archives.msdos.announce
  9. Moderating  at  garbo.uwasa.fi anonymous FTP archives 128.214.87.1
  10. Faculty of Accounting & Industrial Management; University of Vaasa
  11. Internet:  ts@uwasa.fi  Bitnet:  salmi@finfun; FI-65101,   Finland
  12.  
  13. -------------------------------------------------------------------
  14. 31) How does one store, and then restore the original screen?
  15. 32) How can I convert a TPU unit of one TP version to another?
  16. 33) Which error is e.g. Runtime error 205, etc
  17. 34) Why can't I open read-only files? I get "File access denied".
  18. 35) How do I obtain high and low parts of a byte variable?
  19. 36) How can I set a hi-intensity color background in the text mode?
  20. 37) Where can I find a program to convert (Turbo) Pascal to C?
  21. 38) How can I read input without echoing to the screen?
  22. 39) How can I edit the readln input stream?
  23. 40) How can I write (brand) something into my executables?
  24. 41) What is wrong with my program? It hangs without a clear pattern?
  25. 42) How do I convert a decimal word into a hexadecimal string, etc?
  26. 43) How to determine the last drive?
  27. 44) How can I put a running clock into my Turbo Pascal program?
  28. 45) How to establish if a name refers to a directory or not?
  29. 46) How does one disable alt-ctrl-del?
  30. 47) How can I test whether a file exists?
  31. 48) What is the name of the current Turbo Pascal program?
  32. 49) How is the code for rebooting the PC written in Turbo Pascal?
  33. 50) How can I write inline code?
  34. 51) I am running out of memory when compiling my large program.
  35. 52) How do I avoid scrolling in the last column of the last row?
  36. -------------------------------------------------------------------
  37.  
  38. From ts@uwasa.fi Wed Aug 18 00:00:31 1993
  39. Subject: Saving the screen
  40.  
  41. 31. *****
  42.  Q: How does one store, and then restore the original screen?
  43.  
  44.  A: Here is a simple outline for storing and restoring a text mode
  45. screen. Note that the code below is incomplete in a sense that it
  46. works for a color monitor only, because the monochrome screen
  47. address is $B000:$0000.
  48.    For storing and restoring the graphics screen see Ohlsen & Stoker
  49. (1989), Turbo Pascal Advanced Techniques, Que, pp 333-337.
  50.   uses Crt;
  51.   type ScreenType = array [1..4000] of byte;        (* 2 x 80 x 25 *)
  52.   var ColorScreen : ScreenType Absolute $B800:$0000;
  53.       SavedScreen : ScreenType;
  54.       posx, posy : byte;
  55.   begin
  56.     SavedScreen := ColorScreen;      (* Save the screen *)
  57.     posx := WhereX; posy := WhereY;  (* Save the cursor position *)
  58.     writeln ('A simple demo storing and restoring the color text screen');
  59.     writeln ('By Prof. Timo Salmi, ts@uwasa.fi');
  60.     writeln; write ('Press <-'''); readln;
  61.     ColorScreen := SavedScreen;   (* Restore the screen *)
  62.     GotoXY(posx,posy);            (* Go to the stored cursor position *)
  63.   end.
  64. If you would prefer not using the Crt unit, you can apply WHEREXFN,
  65. WHEREYFN, and GOATXY from TSUNTG.TPU from /pc/ts/tspa33*.zip.
  66. Likewise, if you wish to test for the monitor type, that is choose
  67. between $B800:$0000 and $B000:$0000 bases, you can use
  68.  MONOFN "Is it a monochrome video adapter"
  69. in the said units collection.
  70. --------------------------------------------------------------------
  71.  
  72. From ts@uwasa.fi Wed Aug 18 00:00:32 1993
  73. Subject: Converting TPUs
  74.  
  75. 32. *****
  76.  Q: How can I convert a TPU unit of one TP version to another?
  77.  
  78.  A: Forget it. In practical terms such a conversion is not on. The
  79. Turbo Pascal TPU units are strictly version dependent. If there were
  80. a working solution I assume we would have heard of it long since.
  81. The hacks that have been tried won't solve this dilemma. For all
  82. practical purposes you need the source code and the relevant
  83. compiler version.
  84.    You may nevertheless wish to ascertain for which version a TPU
  85. unit has been compiled. This is very simple. Just look at the first
  86. four character of a TPU file. The codes are
  87.  TPU0  for 4.0
  88.  TPU5  for 5.0
  89.  TPU6  for 5.5
  90.  TPU9  for 6.0
  91.  TPUQ  for 7.0 real mode
  92. But don't go editing these. It will not get you anywhere.
  93. --------------------------------------------------------------------
  94.  
  95. From ts@uwasa.fi Wed Aug 18 00:00:33 1993
  96. Subject: Finding about runtime errors
  97.  
  98. 33. *****
  99.  Q: Which error is e.g. Runtime error 205
  100.  
  101.  A: Basically this is a case of RTFM (read the f*ing manual). But it
  102. is very easy to find out even without resorting to the manual. Put
  103. temporarily the statement RunError (205); as the first statement of
  104. your program. Then run your program from the Turbo Pascal IDE, that
  105. is from within the TP editor. The description of the error will
  106. appear.
  107.    If you run a program from within a Turbo Pascal IDE, it is
  108. advisable to turn on the debug options on. You'll get both the error
  109. number and the description. Furthermore by pressing F1 after the
  110. error you get its description in a more verbal format.
  111.    One further trick is to put "uses TSERR"; (Include verbal
  112. run-time error messages) into your program. If you do that, the
  113. run-time errors will be given with a verbal description not just as
  114. a number. TSERR.TPU is part of my TPU collection /pc/ts/tspa*.zip.
  115. --------------------------------------------------------------------
  116.  
  117. From ts@uwasa.fi Wed Aug 18 00:00:34 1993
  118. Subject: Opening read-only files
  119.  
  120. 34. *****
  121.  Q: Why can't I open read-only files? I get "File access denied".
  122.  
  123.  A: The answer is rather simple, but it is not well displayed in the
  124. manuals. In order to read a read-only file you have to set the
  125. FileMode as 0 like below. Else you'll get runtime error 005 "File
  126. access denied".
  127.   var f      : text;          (* Can be any file type *)
  128.       savefm : byte;
  129.   begin
  130.     savefm := FileMode;       (* Save the current FileMode status *)
  131.     FileMode := 0;            (* The default is 2 *)
  132.     assign (f, 'readonly.txt');
  133.     reset (f);
  134.     { have your wicked ways }
  135.     close (f);
  136.     FileMode := savefm;       (* Restore the original FileMode *)
  137.   end.
  138. --------------------------------------------------------------------
  139.  
  140. From ts@uwasa.fi Wed Aug 18 00:00:35 1993
  141. Subject: Getting a nybble from a byte
  142.  
  143. 35. *****
  144.  Q: I have a variable of type BYTE and would like to extract two
  145. numbers from it. (The first 4 bits making up number A, the second 4
  146. bits making up number B).  How can I extract these two numbers?
  147.  
  148.  A: Ah, this questions bring back the good bad old days of the
  149. Commodore C64 programming when bit operations were rather a rule
  150. than a exception. Here is the solution.
  151.   function HIBYTEFN (x : byte) : byte;
  152.   begin
  153.     hibytefn := x Shr 4;           (* Shift right by four bits *)
  154.   end;
  155.   {}
  156.   function LOBYTEFN (x : byte) : byte;
  157.   begin
  158.     lobytefn := x and 15;          (* x and 00001111 *)
  159.   end;
  160. From Patrick Taylor (exuptr@exu.ericsson.se): Ah, leave it to Timo
  161. to come up with a different way! An other is (n div 16)
  162. (n mod 16).
  163.    Patrick is right.  But unless the compiler is optimized, the
  164. former produces more efficient code. Not that it really makes any
  165. practical difference whatsoever.
  166.    Of course the fastest code is produced using assembler as pointed
  167. out by Maarten Pennings (maarten@cs.ruu.nl) who provided the
  168. following inline example:
  169.   function high(b:byte):byte;
  170.     inline($58         { POP AX      | AH=?, AL=b       }
  171.           /$30/$e4     { XOR AH,AH   | AH=0, AL=b       }
  172.           /$b9/$04/$00 { MOV CX,0004 | AH=0, AL=b, CL=4 }
  173.           /$d3/$e8     { SHR AX,CL   | AX=b shr 4       }
  174.           );
  175.  
  176.  A2: Getting a word from a longint can alternatively be achieved
  177. without any calculations by using a kind of typecasting. Below is
  178. the code I have utilized in garbo.uwasa.fi:/pc/tspa*.zip.
  179.   (* Get the high-order word of the longint argument *)
  180.   function HIWORDFN (x : longint) : word;
  181.   type type1 = record
  182.                  low  : word;
  183.                  high : word;
  184.                end;
  185.   var m1 : type1 absolute x;
  186.   begin
  187.     hiwordfn := m1.high;
  188.   end;  (* hiwordfn *)
  189. --------------------------------------------------------------------
  190.  
  191. From ts@uwasa.fi Wed Aug 18 00:00:36 1993
  192. Subject: Setting hi-intensity background
  193.  
  194. 36. *****
  195.  Q: How can I set a hi-intensity color background in the text mode?
  196.  
  197.  A: As you should know, the you can test for a blinking text for
  198. example as follows.
  199.   uses Crt;
  200.   begin
  201.     TextColor (11 + 128);  (* or LightCyan + Blink *)
  202.     TextBackground (Blue);
  203.     writeln ('What''s the catch?');  (* An aside, note the '' pair *)
  204.   end.
  205. In the above, bit 7 (the 128) controls the blinking. If you have at
  206. least an EGA, you can alter the interpretation of the highest text
  207. color bit to denote a hi-intensity background, but then you lose the
  208. the blinking. The following piece of code disables blinking,
  209. enabling a hi-intensity background.
  210.   uses Dos;
  211.   var regs : registers;
  212.   begin
  213.     FillChar (regs, SizeOf(regs), 0); (* An initialization precaution *)
  214.     regs.ah := $10;                   (* Function $10 *)
  215.     regs.al := $03;                   (* Subfunction $03 *)
  216.     regs.bl := $00;
  217.     Intr ($10, regs);      (* ROM BIOS video driver interrupt *)
  218.   end.
  219. To enable blinking again, set regs.bl := $01; Any high-intensity
  220. background you may have currently on the screen, will instantly
  221. change into a blinking text a a low-intensity background.
  222.  
  223.  A2: The previous answer assumes at least an EGA. Otherwise ports
  224. must be accessed. This is both advanced and dangerous programming,
  225. because errors in handling posts can do real harm. Besides it is
  226. fair to require at least an EGA in writing modern programs, at least
  227. for non-laptops, and on the latter the colors don't really matter
  228. for CGA and below. Let's take a look, nevertheless, how this is done
  229. for a CGA. Note that this won't work an an EGA and beyond, not at
  230. least in my tests. For detecting the video adapter you have, see the
  231. DetectGraph procedure in you Turbo Pascal manual.
  232.    First we need some basics from MEMORY.LST in Ralf Brown's
  233. garbo.uwasa.fi:/pc/programming/inter35b.zip (or whatever version is
  234. current):
  235.  Format of BIOS Data Segment at segment 40h:
  236.   63h WORD Video CRT controller base address: color=03D4h, mono=03B4h
  237.   65h BYTE Video current setting of mode select register 03D8h/03B8h
  238. From David Jurgens's /pc/programming/helppc21.zip we see
  239.   3D0-3DF Color Graphics Monitor Adapter (ports 3D0-3DB are
  240.           write only, see 6845)
  241.   3D8 6845 Mode control register (CGA, EGA, VGA, except PCjr)
  242. From Darryl Friesen's (friesend@jester.usask.ca) in comp.lang.pascal
  243. we have, the following procedure, with my own added comments (* *).
  244.   procedure SetBlinkState (state : boolean);
  245.   var ModeRegPort : word;
  246.       ModeReg     : byte;
  247.   begin
  248.     Inline($FA); { CLI }           (* Interrupts off *)
  249.     ModeRegPort := MemW[$0040:$0063]+4;  (* Typically $03D4+4 = $03D8 *)
  250.     ModeReg := Mem[$0040:$0065];   (* Typically 1001 *)
  251.     if state then                  (* Bit 5 controls blink enable *)
  252.       ModeReg := ModeReg or $20    (* $20 = 00100000 (base2) *)
  253.     else
  254.       ModeReg := ModeReg and $DF;  (* $DF = 11011111 disable *)
  255.     Port[ModeRegPort] := ModeReg;  (* Typically $9 = 00001001 *)
  256.     Mem[$0040:$0065] := ModeReg;   (*       or $29 = 00101001 *)
  257.     Inline($FB) { STI }            (* Interrupts on *)
  258.   end;
  259. --------------------------------------------------------------------
  260.  
  261. From ts@uwasa.fi Wed Aug 18 00:00:37 1993
  262. Subject: Pascal to C
  263.  
  264. 37. *****
  265.  Q: Where can I find a program to convert (Turbo) Pascal to C?
  266.  
  267.  A: This is a relevant question, but I have placed elsewhere the
  268. tips on the "looking for a program" questions. Here are the
  269. pointers to further pointers :-). (The FAQ versions might have been
  270. updated since I wrote this.)
  271.  garbo.uwasa.fi:/pc/pd2/camfaq.zip
  272.  camfaq.zip comp.archives.msdos.(d/announce) FAQ (general finding)
  273.  :
  274.  garbo.uwasa.fi:/pc/pd2/tsfaqn37.zip
  275.  tsfaqn37.zip Questions from UseNet and Timo's answers
  276.  :
  277.  garbo.uwasa.fi:/pc/pd2/faquote.zip
  278.  faquote.zip Old information from tsfaq Frequently Asked Questions
  279. --------------------------------------------------------------------
  280.  
  281. From ts@uwasa.fi Wed Aug 18 00:00:38 1993
  282. Subject: Turning off the input echo
  283.  
  284. 38. *****
  285.  Q: How can I read input without echoing to the screen?
  286.  
  287.  A: It is fairly simple. Study this example source code, with the
  288. manual, if need be.
  289.   uses Crt;
  290.   var password : string;
  291.   {}
  292.   (* Read without echoing *)
  293.   procedure GETPASS (var s : string);
  294.   var key : integer;
  295.       ch : char;
  296.   begin
  297.     s := '';
  298.     repeat
  299.       ch := ReadKey; key := ord (ch);
  300.       case key of
  301.          0 : ch := ReadKey;  (* Discard two-character keys, like F1 *)
  302.         13 : exit;           (* Enter has been pressed *)
  303.         1..12,13..31,255 :;  (* Discard the special characters *)
  304.         else s := s + ch;
  305.       end;
  306.    until false;
  307.   end;  (* getpass *)
  308.   {}
  309.   (* The main program *)
  310.   begin
  311.     write ('Password: ');
  312.     GETPASS (password);
  313.     writeln;
  314.     writeln (password);
  315.   end.
  316.   {}
  317. If you wish to be able to edit the input stream, like having the
  318. BackSpace functional, that is more complicated, and is left as an
  319. exercise after these basics. A hint: 8 : Delete (s, Length(s), 1);
  320. --------------------------------------------------------------------
  321.  
  322. From ts@uwasa.fi Wed Aug 18 00:00:39 1993
  323. Subject: Input line-editing
  324.  
  325. 39. *****
  326.  Q: How can I edit the readln input stream?
  327.  
  328.  A: In practice, if you wish to use anything beyond simple the
  329. BackSpace deleting, you'll have to build your own line editing
  330. routines expanding on the code in the previous item. It is quite a
  331. task, and you can alternatively find the preprogrammed routines in
  332. my Turbo Pascal units tspa33*.zip (or whatever version number is
  333. current).
  334.  EDRDEBLN Editable Readln with ctrl-c, break trapping, pre-fill etc
  335.  EDRDEFLN Editable Readln with recall, pre-fill, and insert toggle
  336.  EDRDLN   Readln with line-editing potential (the simplest)
  337.  EDREABLN Edreadln with ctrl-c and break trapping
  338.  EDREADLN Editable Readln with recall, and insert toggle
  339. --------------------------------------------------------------------
  340.  
  341. From ts@uwasa.fi Wed Aug 18 00:00:40 1993
  342. Subject: Executable branding
  343.  
  344. 40. *****
  345.  Q: How can I write (brand) something into my executables?
  346.     Here is the actual question that led me to writing this item: 'I
  347.     am very interested in the .EXE "branding" techniques you use in
  348.     your TSUNTI unit. Would it be possible to get hold of the source
  349.     code for that unit, as it would save me from having to re-invent
  350.     the wheel?'
  351.  
  352.  A: What you are referring to is
  353.  BRANDEXE Store information within your program's .exe file (MsDos 3.0+)
  354.  CHKSUMFN Checksum selftest to detect any tampering (MsDos 3.0+)
  355.  USECOUNT Get the number of times the program has been used
  356. Sorry no, I don't want to distribute my /pc/turbopas/tspa33*.zip
  357. source codes.  Besides they would be less useful to you than you may
  358. think because internally my programs are in Finnish, comments,
  359. variable and procedure names, and all. But I can hopefully help you
  360. by giving a reference to a similar code.  Please see Ohlsen &
  361. Stoker, Turbo Pascal Advanced Techniques, Que, 1989, p. 420.
  362. --------------------------------------------------------------------
  363.  
  364. From ts@uwasa.fi Wed Aug 18 00:00:41 1993
  365. Subject: Elusive, inconsistent errors
  366.  
  367. 41. *****
  368.  Q: What is wrong with my program? It hangs without a clear pattern?
  369.  
  370.  A: With experience one learns that some programming errors are very
  371. elusive. I have many times seen users declaring that they have found
  372. a bug in Turbo Pascal, but in the overwhelming majority of cases it
  373. still is just a programming error, which just is more difficult to
  374. find than the more clear-cut cases. When you have symptoms like your
  375. program crashing from within the IDE, but working seemingly all
  376. right when called as stand-alone, or something equally strange, you
  377. might have one of the following problems.
  378. - A variable or some variables in your code are uninitialized thus
  379.   getting random values, which differ depending on your environment.
  380. - Your indexes are overflowing. Set on the range check {$R+}
  381.   directive for testing.
  382. - An error in the pointer logic.
  383. Normal debugging does not necessarily help in locating these errors
  384. because one is easily led to debugging the wrong parts of one's
  385. program. Especially the latter two reasons can cause errors which
  386. seemingly have nothing to do with the actual cause. This results
  387. from the fact that indexing and pointer errors can overwrite parts
  388. of memory causing strange quirks in your program. If you have used
  389. indexing with {$R-} or if you use pointer operations, sooner or
  390. later you are bound to have these problems in developing your
  391. applications.
  392. --------------------------------------------------------------------
  393.  
  394. From ts@uwasa.fi Wed Aug 18 00:00:42 1993
  395. Subject: Converting the number base
  396.  
  397. 42. *****
  398.  Q: How do I convert a decimal word into a hexadecimal string, etc?
  399.  
  400.  A: Here is one possibility
  401.   function HEXFN (decimal : word) : string;
  402.   const hexDigit : array [0..15] of char = '0123456789ABCDEF';
  403.   begin
  404.     hexfn := hexDigit[(decimal shr 12)]
  405.           + hexDigit[(decimal shr 8) and $0F]
  406.           + hexDigit[(decimal shr 4) and $0F]
  407.           + hexDigit[(decimal and $0F)];
  408.   end;  (* hexfn *)
  409. Here is another conversion example (from longint to binary string)
  410.   function LBINFN (decimal : longint) : string;
  411.   const BinDigit : array [0..1] of char = '01';
  412.   var i     : byte;
  413.       binar : string;
  414.   begin
  415.     FillChar (binar, SizeOf(binar), ' ');
  416.     binar[0] := chr(32);
  417.     for i := 0 to 31 do
  418.       binar[32-i] := BinDigit[(decimal shr i) and 1];
  419.     lbinfn := binar;
  420.   end;  (* lbinfn *)
  421. For a full set of conversions, both from and to decimal, apply
  422. TSUTNTB.TPU from garbo.uwasa.fi:/pc/ts/tspa*.zip.
  423. --------------------------------------------------------------------
  424.  
  425. From ts@uwasa.fi Wed Aug 18 00:00:43 1993
  426. Subject: Identifying the last drive
  427.  
  428. 43. *****
  429.  Q: How to determine the last drive?
  430.  
  431.  A: One way of doing that is utilizing the information in DPB, that
  432. is the Drive Parameter Block, but that is rather complicated, so you
  433. can find that without source code in garbo.uwasa.fi:/pc/ts/tspa*.zip
  434. in the TSUNTH unit.
  435.  Another way is using interrrupt 21H, function 36H to detect if a
  436. drive exists starting from the first drive letter. The code is given
  437. below. The disadvantage of this method is that it does not
  438. distinguish between real and substituted drives.
  439.   uses Dos;
  440.   function LASTDFN : char;  (* Detect last harddisk letter *)
  441.   var regs : registers;
  442.       i    : byte;
  443.   begin
  444.     i := 2;
  445.     repeat
  446.       Inc(i);
  447.       FillChar (regs, SizeOf(regs), 0);
  448.       regs.ah := $36;
  449.       regs.dl := i;
  450.       MsDos(regs);
  451.     until (regs.ax = $FFFF);
  452.     lastdfn := chr(i+63);
  453.   end;  (* lastdfn *)
  454. --------------------------------------------------------------------
  455.  
  456. From ts@uwasa.fi Wed Aug 18 00:00:44 1993
  457. Subject: Clock display in a TP program
  458.  
  459. 44. *****
  460.  Q: How can I put a running clock into my Turbo Pascal program?
  461.  
  462.  A: We are not speaking of a stand-alone TSR-clock (which is a
  463. different task), but considering a clock that continuously displays
  464. the time in some part of the output screen of your Turbo Pascal
  465. program.
  466.     You might first want to read the earlier items about ReadKey
  467. usages if you are not familiar with it (you probably are, because
  468. you would not pose this advanced question if you were a novice). The
  469. items are the unlikely "How do I disable or capture the break key in
  470. Turbo Pascal?" and "How can I read input without echoing to the
  471. screen?"
  472.    The general idea is to make the body of the program a repeat
  473. until loop using ReadKey for input and updating the clock display
  474. at suitable junctions within the loop. The scheme is thus something
  475. like the following.
  476.   procedure showtime;
  477.     begin
  478.       { if the second has changed, write the time }
  479.     end;
  480.   :
  481.   repeat
  482.     { do whatever }
  483.     showtime;
  484.     if KeyPressed then
  485.       case ReadKey of
  486.         { whatever }
  487.         { exit rules }
  488.       end;
  489.     showtime;
  490.     :
  491.     showtime;
  492.   until false;
  493.    One trick of the trade is that you must not update your clock
  494. each time the clock routine is encountered. You should test if the
  495. second has changed, and update only then. Else you are liable to get
  496. an annoying flicker in your clock.
  497. --------------------------------------------------------------------
  498.  
  499. From ts@uwasa.fi Wed Aug 18 00:00:45 1993
  500. Subject: Is a name a directory
  501.  
  502. 45. *****
  503.  Q: How to establish if a name refers to a directory or not?
  504.  
  505.  A: This question has turned out a bit more complicated than I first
  506. thought. There are several methods, each with some catch. The first
  507. is trying to open the name as a file and observing the IOResult. The
  508. ISDIRFN function in garbo.uwasa.fi:/pc/ts/tspa*.zip TPU unit
  509. TSUNTJ.TPU is based on this method. Unfortunately it is not always
  510. stable. I have been reported problems in connection with DRDOS by
  511. Richard Breuer (ricki@pool.informatik.rwth-aachen.de) who has
  512. tested these routines.
  513.   The second method (ISDIR2FN) is based on the fact that the file
  514. NUL exists in a directory if the directory exists.
  515.   The thrid method (ISDIR3FN) is a brute force method. It is given
  516. below, since it is quite an instructive little exercise of Turbo
  517. Pascal programming.
  518.   (* Search recursively through a drive's directories.
  519.      Auxiliary, recursive procedure for ISDIR3FN *)
  520.   procedure SEARCHDR (Path, FileSpec : string;
  521.                       name           : string;
  522.                       var found      : boolean);
  523.   var FileInfo : SearchRec;
  524.   begin
  525.     FindFirst (Path + '*.*', Directory, FileInfo);
  526.     while DosError = 0 do
  527.       begin
  528.         if ((FileInfo.Attr and Directory) > 0) and
  529.             (FileInfo.Name <> '.') and
  530.             (FileInfo.Name <> '..') then
  531.               begin
  532.                 SEARCHDR (Path + FileInfo.Name + '\',
  533.                           FileSpec,
  534.                           name,
  535.                           found);
  536.                 if Path + FileInfo.Name + '\' = name then
  537.                   found := true;
  538.               end;
  539.         FindNext (FileInfo);
  540.       end; {while}
  541.   end;  (* searchdr *)
  542.  
  543.   (* Does a name refer to a directory *)
  544.   function ISDIR3FN (name : string) : boolean;
  545.   var drive : char;
  546.       found : boolean;
  547.   begin
  548.     {... Default value ...}
  549.     isdir3fn := false;
  550.     {... Discard empty names ...}
  551.     if name = '' then exit;
  552.     {... Expand into a fully qualified name, makes it uppercase ...}
  553.     name := FExpand (name);
  554.     if name[Length(name)] <> '\' then name := name + '\';
  555.     {... Extract the drive letter from the name ...}
  556.     drive := UpCase (name[1]);
  557.     {... Check first for the root ...}
  558.     if drive + ':\' = name then
  559.       begin isdir3fn := true; exit; end;
  560.     {... Check the rest of the directories recursively ...}
  561.     found := false;
  562.     SEARCHDR (drive + ':\', '*.*', name, found);
  563.     isdir3fn := found;
  564.   end;  (* isdir3fn *)
  565. --------------------------------------------------------------------
  566.  
  567. From ts@uwasa.fi Wed Aug 18 00:00:46 1993
  568. Subject: Disabling alt-ctrl-del
  569.  
  570. 46. *****
  571.  Q: How does one disable alt-ctrl-del?
  572.  
  573.  A: I can only give a pointer to source code. Take a look at the
  574. code by Mikko Hanninen in garbo.uwasa.fi:/pc/turbopas/cadthf10.zip.
  575.    I have utilized alt-ctrl-del disabling at least in one of my own
  576. programs (PESTIKID.EXE). The code is not available, but the general
  577. idea is replacing the old keyboard interrupt ($09) with a handler of
  578. one's own. If the handler detects alt-ctrl-del, the keyboard is
  579. reset, else the handler is chained back to the original interrupt.
  580. The chaining requires a rather complicated inline procedure provided
  581. in TurboPower Software's kit. An additional complication is that the
  582. del keypress must be intercepted already at the relevant port $60,
  583. and the alt and ctrl status must be tested, so that the rebooting
  584. will not be invoked. Resetting the keyboard requires accessing the
  585. $20 and $61 ports.
  586. --------------------------------------------------------------------
  587.  
  588. From ts@uwasa.fi Wed Aug 18 00:00:47 1993
  589. Subject: Does a file exist
  590.  
  591. 47. *****
  592.  Q: How can I test whether a file exists?
  593.  
  594.  A: There are several alternatives. Here is the most common with
  595. example code. It recognizes also read-only, hidden and system files.
  596.   function FILEXIST (name : string) : boolean;
  597.   var fm : byte;
  598.       f  : file;
  599.       b  : boolean;
  600.   begin
  601.     fm := FileMode;
  602.     FileMode := 0;
  603.     assign (f, name);
  604.     {$I-} reset(f); {$I+}
  605.     b := IOResult = 0;
  606.     if b then close(f);
  607.     filexist := b;
  608.     FileMode := fm;
  609.   end;
  610.  
  611. A second alternative is
  612.   Uses Dos;
  613.   function FILEXIST (name : string) : boolean;
  614.   var f  : file;
  615.       a  : word;
  616.   begin
  617.     assign (f, name);
  618.     GetFAttr (f, a);
  619.     filexist := false;
  620.     if DosError = 0 then
  621.       if ((a and Directory) = 0) and ((a and VolumeId) = 0) then
  622.         filexist := true;
  623.   end;
  624.  
  625. A third alternative is
  626.   Uses Dos;
  627.   function FILEXIST (name : PathStr) : boolean;
  628.   begin
  629.     filexist := FSearch (name, '') <> '';
  630.   end;
  631.  
  632. A fourth alternative is the following. Be careful with this option,
  633. since it works a bit differently from the others. It accepts wild
  634. cards. Thus, for example FILEXIST('c:\autoexec.*') would be TRUE in
  635. this method, while FALSE in all the above.
  636.   Uses Dos;
  637.   function FILEXIST (name : string) : boolean;
  638.   var f : SearchRec;
  639.   begin
  640.     filexist := false;
  641.     FindFirst (name, AnyFile, f);
  642.     if DosError = 0 then
  643.       if (f.attr <> Directory) and (f.attr <> VolumeId) then
  644.         filexist := true;
  645.   end;
  646. A good variation from KDT@newton.national-physical-lab.co.uk of this
  647. theme, disallowing wildcards:
  648.   function file_exists (fname :string) :boolean;
  649.   var f :searchrec;
  650.   begin
  651.     findfirst (fname, anyfile - directory - volumeid, f);
  652.     file_exists := (doserror + pos('*',fname) + pos('?',fname) = 0);
  653.   end;
  654.  
  655. --------------------------------------------------------------------
  656.  
  657. From ts@uwasa.fi Wed Aug 18 00:00:48 1993
  658. Subject: The current program name
  659.  
  660. 48. *****
  661.  Q: What is the name of the current Turbo Pascal program?
  662.  
  663.  A: The name of the currently executing Turbo Pascal program is in
  664. ParamStr(0).
  665.    This was introduced in TP version 5.0, and as far as I recall at
  666. least MsDos version 3.0 is required. For TP 4.0 you can use
  667. "ParamStr0 The name of the program" from TSUNT45 in garbo.uwasa.fi:
  668. /pc/ts/tspa3340.zip (or whatever the version number is the latest).
  669.    It is advisable to put the value into a string variable at be
  670. beginning of the program before eny I/O takes place. Thus you might
  671. wish to use:
  672.   var progname : string;
  673.   begin  { the main program }
  674.     progname := ParamStr(0);
  675.     :
  676. A bonus of this method is that you can access the individual
  677. characters of progname (e.g. progname[1] for the drive) while that
  678. is not possible to do for the ParamStr keyword.
  679. --------------------------------------------------------------------
  680.  
  681. From ts@uwasa.fi Wed Aug 18 00:00:49 1993
  682. Subject: How can a program reboot my PC?
  683.  
  684. 49. *****
  685.  Q: How is the code for rebooting the PC written in Turbo Pascal?
  686.  
  687.  A: This item draws from the information and the C-code example in
  688. Stan Brown's comp.os.msdos.programmer FAQ, garbo.uwasa.fi:
  689. /pc/doc-net/faqp9312.zip (at the time of writing this), from
  690. memory.lst and interrup.b in /pc/programming/inter35b.zip, and from
  691. /pc/programming/helppc21.zip. The Turbo Pascal code is my adaptation
  692. of the C-code. It is not a one-to-one replica.
  693.    The usually advocated warm-boot method is storing $1234 in the
  694. word at $0040:$0072 and jumping to address $FFFF:$0000. The problem
  695. with this approach is that files must first be closed, potential
  696. caches flushed. This is how to do this
  697.   procedure REBOOT;
  698.   label next;
  699.   var regs  : registers;
  700.       i     : byte;
  701.       ticks : longint;
  702.   begin
  703.     {... "press" alt-ctrl ...}
  704.     mem[$0040:$0017] := mem[$0040:$0017] or $0C;  { 00001100 }
  705.     {... "press" del, try a few times ...}
  706.     for i := 1 to 10 do
  707.       begin
  708.         FillChar (regs, sizeOf(regs), 0);  { initialize }
  709.         regs.ah := $4F;  { service number }
  710.         regs.al := $53;  { del key's scan code }
  711.         regs.flags := FCarry;  { "sentinel for ignoring key" }
  712.         Intr ($15, regs);
  713.         {... check if the del key registered, if not retry ...}
  714.         if regs.flags and Fcarry > 0 then goto next;
  715.         {... waste some time, watch out for midnight ...}
  716.         ticks := MemL [$0040:$006C];
  717.         repeat until (MemL[$0040:$006C] - ticks > 3) or
  718.                      (MemL[$0040:$006C] - ticks < 0)
  719.     end; {for}
  720.     exit;
  721.   next:
  722.     {... disk reset: writes all modified disk buffers to disk ...}
  723.     FillChar (regs, sizeOf(regs), 0);
  724.     regs.ah := $0D;
  725.     MsDos (regs);
  726.     {... set post-reset flag, use $0000 instead of $1234 for coldboot ...}
  727.     memW[$0040:$0072] := $1234;
  728.     {... jump to $FFFF:0000 BIOS reset ...}
  729.     Inline($EA/$00/$00/$FF/$FF);
  730.   end;  (* reboot *)
  731. One slight problem with this approach is that the keyboard intercept
  732. interrupt $15 service $4F requires at least an AT according to
  733. inter35b.zip. A simple test based on "FFFF:E byte ROM machine id"
  734. (the previous definition is from helppc21.zip) is:
  735.   function ISATFN : boolean;
  736.   begin
  737.      case Mem[$F000:$FFFE] of
  738.        $FC, $FA, $F8 : isatfn := true;
  739.        else isatfn := false;
  740.      end; {case}
  741.   end;  (* isatfn *)
  742. For a more comprehensive test use CPUFN "Get the type of the
  743. processor chip" from TSUNTH in garbo.uwasa.fi:/pc/ts/tspa*.zip.
  744. --------------------------------------------------------------------
  745.  
  746. From ts@uwasa.fi Wed Aug 18 00:00:50 1993
  747. Subject: Writing inline code
  748.  
  749. 50. *****
  750.  Q: How can I write inline code?
  751.  
  752.  A: In Turbo Pascal versions prior 6.0 assembler code could not be
  753. directly included in the code. Instead one had to assemble the code
  754. into inline statements. Consider the task of rebooting the PC
  755. (without disk closing and cache flushing).  The assemble code for
  756. this is
  757.   mov ax,40
  758.   mov ds,ax
  759.   mov wo [72],1234
  760.   jmp FFFF:0000
  761. To assemble this code into an inline statement write the following
  762. file calling it e.g. debug.in.  The empty line is important.
  763.   .... begin debug.in, cut here ....
  764.   a 100
  765.   mov ax,40
  766.   mov ds,ax
  767.   mov wo [72],1234
  768.   jmp FFFF:0000
  769.  
  770.   u 100
  771.   q
  772.   .... end debug.in, cut here ....
  773. Give the following command
  774.   debug < debug.in
  775. You'll get
  776.   0E9E:0100 B84000        MOV     AX,0040
  777.   0E9E:0103 8ED8          MOV     DS,AX
  778.   0E9E:0105 C70672003412  MOV     WORD PTR [0072],1234
  779.   0E9E:010B EA0000FFFF    JMP     FFFF:0000
  780. This translates into
  781.     Inline ($B8/$40/$00/
  782.             $8E/$D8/
  783.             $C7/$06/$72/$00/$34/$12/
  784.             $EA/$00/$00/$FF/$FF);
  785. --------------------------------------------------------------------
  786.  
  787. From ts@uwasa.fi Wed Aug 18 00:00:51 1993
  788. Subject: Out of memory in compiling
  789.  
  790. 51. *****
  791.  Q: I am running out of memory when compiling my large program. What
  792.     can I do?
  793.  
  794.  A: If you are compiling your program from within the IDE (the
  795. Integrated Development Environment) then select invoke the Option
  796. from the main menu, choose the Compiler item and set the Link buffer
  797. to Memory. (Also make the Compile option Destination to be Disk).
  798.    If this is not sufficient, next resort to using the TPC command
  799. line version of the Turbo Pascal compiler instead of the IDE.  Use
  800. the /L option ("Link buffer on disk").  Other users have also
  801. pointed to using the protected mode version, TPCX, if you have it.
  802.    Divide your program into units. It is advisable anyway for
  803. modularity when your program size grows.
  804.    I have no experience with this alternative, but other users have
  805. pointed that "If you have some extended memory, switch to BP", that
  806. is Borland Pascal 7.0.
  807.  
  808.  A2: If you would prefer compiling your program from within the IDE
  809. but cannot do it for the above reason (or if you would prefer to
  810. compile your program from within your favorite editor instead of the
  811. TP IDE) you can use the following trick. If your editor has a macro
  812. language like most good editors do, the assign a hotkey macro that
  813. compiles the current file with the TPC. If you are using SemWare's
  814. QEdit editor you'll find such a macro in garbo.uwasa.fi:/pc/ts/
  815. tsqed17.zip ("Macros and configurations for QEdit text-editor").
  816.    Also your editor must be swapped to disk during the compilation
  817. if memory is critical. There is a very good program for doing that:
  818. /pc/sysutil/shroom2d.zip ("Shell Room, Swap to disk when shelling to
  819. application"). For example I invoke the QEdit editor with using the
  820. following batch:
  821.  c:\tools\shroom -s r:\cmand -z 1024 c:\qedit\q %1 %2 %3 %4 %5 %6 %7
  822. You'll find more about the switches in the Shell Room documentation.
  823. The -s switch designates the swap destination (my r:\cmand directory
  824. is on my ramdisk). The -z switch sets the shell environment size.
  825.   An unfortunate part is that the Turbo Pascal IDE is about the only
  826. program I know that is not amenable the to Shell Room utility, so
  827. you cannot utilize Shell Room to swap the TP IDE to disk.
  828. --------------------------------------------------------------------
  829.  
  830. From ts@uwasa.fi Wed Aug 18 00:00:52 1993
  831. Subject: Last position write woes
  832.  
  833. 52. *****
  834.  Q: How do I avoid scrolling in the last column of the last row?
  835.  
  836.  A: If you use write or writeln at the last column of the last row
  837. (usually 80,25) the screen will scroll. If you wish to avoid the
  838. scrolling you'll have to use an alternative write that does not move
  839. the cursor. Here is a procedure to write without moving the cursor
  840.   uses Dos;
  841.   procedure WriteChar (Character : char; fgColor, bgColor : byte);
  842.   var r : registers;
  843.   begin
  844.     FillChar (r, SizeOf(r), 0);
  845.     r.ah := $09;
  846.     r.al := ord(Character);
  847.     r.bl := (bgColor shl 4) or fgColor;
  848.     r.cx := 1;    { Number of repeats }
  849.     Intr ($10, r);
  850.   end;  (* writechar *)
  851. Thus, if you wish to write to the last column of the last row, you
  852. must first move the cursor to that position. That can be done in
  853. alternative ways. One might get there by having written previously
  854. on the screen (with writeln and write routines) until one is in that
  855. position. Another alternative is using GoToXY(80,20), but then you
  856. have to use the Crt unit. If you don't want to use it, then you can
  857. move the cursor by employing "GOATXY As the ordinary GoToXY but no
  858. Crt unit required" from garbo.uwasa.fi:/pc/ts/ tspa*.zip.
  859.    There is an alternative interrupt service ($0A) which does the
  860. same as service $09, but uses the default colors instead. Just
  861. substitute $0A for $09, and leave the r.bl assignment out of the
  862. WriteChar routine.
  863.    Another option for writing anyhere on the screen without
  864. affecting the cursor is using direct screen writes:
  865.   uses Dos;
  866.   procedure WriteChar (c : char; x, y : byte; fg, bg : byte);
  867.   var vidstart : word;
  868.       regs     : registers;
  869.   begin
  870.     FillChar (regs, SizeOf(regs), 0);
  871.     regs.ah := $0F;
  872.     Intr ($10, regs);  { Color or MonoChrome video adapter }
  873.     if regs.al = 7 then vidstart := $B000 else vidstart := $B800;
  874.     mem[vidstart:((y-1)*80+x-1)*2] := ord(c);
  875.     mem[vidstart:((y-1)*80+x-1)*2+1] := (bg shl 4) or fg;
  876.   end;
  877. To write to the last postion simply apply e.g.
  878.   WriteChar ('X', 80, 25, 14, 0);  { Yellow on black }
  879. The foreground (fg) and the background (bg) color codes are
  880.   Black        =   0
  881.   Blue         =   1
  882.   Green        =   2
  883.   Cyan         =   3
  884.   Red          =   4
  885.   Magenta      =   5
  886.   Brown        =   6
  887.   LightGray    =   7
  888.   DarkGray     =   8
  889.   LightBlue    =   9
  890.   LightGreen   =  10
  891.   LightCyan    =  11
  892.   LightRed     =  12
  893.   LightMagenta =  13
  894.   Yellow       =  14
  895.   White        =  15
  896.   Blink        = 128
  897. --------------------------------------------------------------------
  898.